/*
 * Copyright 2014 by État de Genève (Switzerland)
 * All rights reserved.
 *
 * Author: ELCA Informatique SA
 */
define([
    'services/layout',
    'plugins/router',
    'services/notifier',
    'services/appConfig',
    'i18n',
    'services/dataservice',
    'services/config',
    'common/breadcrumb',
    'common/menu',
    'plugins/lifecycle',
    'services/user',
    'durandal/app'
],
    function (L, router, notifier, appConfig, i18n, ds, config, breadcrumb, menu, lifecycle, user, app) {

        // helper for directions' string value and inversion of these
        var _dirs = {
            ASC: 'ASC',
            DESC: 'DESC',
            invert: {
                ASC: 'DESC',
                DESC: 'ASC'
            }
        };

        /**
         * listTemplate is responsible for loading the configuration of the clicked list,
         * then choosing the correct view (searchList or inboxList)
         * then binding the data to the view
         */
        var listTemplateCtor = function () {

            // inbox or search based ?
            this.isInboxBased = ko.observable(false);
            // if inbox based, searchInboxOnly is a search option
            this.searchInboxOnly = ko.observable(true);

            // list type (messages, tasks, tiers, etc...)
            this.type = '';
            // type of message
            this.typeMessage = '';
            // if an item's detail form is shown ('id' and 'itemType' present in the route)
            this.visibleItem = ko.observable();

            // holds the array index of the previous and next items in the list of items
            // used only in 'inbox' mode
            this.previousItemIndex = ko.observable(null);
            this.nextItemIndex = ko.observable(null);

            // Check field Attribue a moi for Advanced Search in Messages
            this.isAttribueMoi = ko.observable(false);
            this.userInitials = user.initials;

            // configure this liste depending on type (template, advancedSearch fields, actions, etc...)
            this.config = {};

            // the headers of the dynamic list
            this.columns = ko.observableArray();
            // the columns displayed at any time are not necessary all the columns available, but a subset of them
            this.displayColumns = ko.computed(function () {
                return _.filter(this.columns(), function (col) {
                    // both, extended and short are defined in static/listTypeDef.json
                    return col.displayMode === 'both'
                        || (col.displayMode === 'extended' && !this.visibleItem())
                        || (col.displayMode === 'short' && !!this.visibleItem());

                }.bind(this));
            }, this);
            // determine where the 'index' of each row is
            this.primaryColumnIndex = ko.computed(function () {
                var primary = _.findWhere(this.columns(), {'isPrimary': true});

                if (primary) {
                    return primary.index;
                }

                return 0;
            }, this);
            // try to determine the type of each column
            this.itemTypeColumnIndex = ko.computed(function () {
                var type = _.findWhere(this.columns(), {'isType': true});

                if (type) {
                    return type.index;
                }

                return 'undefined';
            }.bind(this));
            // returns non sortable columns
            this.noSortColumns = ko.computed(function () {
                return _.pluck(_.where(this.columns(), { noSort: true }), 'id');
            }, this);

            // content of the list
            this.items = ko.observableArray();


            // determine currently visible panel ('' for no visible panel)
            // available panes are 'advancedSearch', 'filters', and 'actions'
            this.currentVisiblePanel = ko.observable('');
            // define a boolean property for each panel type, to simplify binding
            this.isAdvancedVisible = ko.computed(function () {
                return this.currentVisiblePanel() === 'advanced';
            }.bind(this));
            this.isFiltersVisible = ko.computed(function () {
                return this.currentVisiblePanel() === 'filters';
            }.bind(this));
            this.isActionsVisible = ko.computed(function () {
                return this.currentVisiblePanel() === 'actions';
            }.bind(this));

            // quicksearch input content
            this.quicksearch = ko.observable();
            // advanced search fields are determined from static/listTypeDef.json
            // and transformed as an observable
            this.advancedSearchFields = ko.observable();
            // default filters fields are from static/listTypeDef.json
            this.defaultFilterFields =  ko.observable();

            this.asErrors = ko.observableArray();

            // wich actions are available (determined from configuration)
            this.actions = [];

            // current item count
            this.totalItemCount = ko.observable();

            // is the action checkbox in the list header checked or not
            this.globalCheck = ko.observable(false);

            // stores indexes corresponding to checked checkboxes: e.g. ["0", "4", "7"]
            // (see knockout 'checked' bindings for details)
            this.checks = ko.observableArray();
            // transforms this.checks in a more useful array: e.g. [true, null, null, null, true, null, null, true]
            this.checkStates = ko.computed(function () {
                var states = [];
                _.each(this.checks(), function (i) {
                    states[+i] = true;
                });
                return states;
            }.bind(this));
            // should checkboxes be visible or not
            this.showBulkSelector = ko.observable(false);

            // paging information
            this.paging = {
                pageSize: config.api.defaultListPageSize,
                pageNbr: ko.observable(1)
            };

            // list sorting information
            this.sorting = ko.observable(null);
            // helper for sort directions
            this.dirs = _dirs;

            // tells if list should be totally hidden when item is opened
            this.hideList = ko.computed(function () {
                // if nothing in the list, but some detail selected: show detail in full width
                return !this.totalItemCount() && !!this.visibleItem();// && this.visibleItem().id === 'new';
            }, this);

            // Current menu of the context
            this.currentMenu = ko.observable();
            // registers two events for next and previous function,
            // so individual forms can call next() and previous() themselves
            app.off('cms:list:next');
            app.on('cms:list:next', openNext.bind(this));
            app.off('cms:list:previous');
            app.on('cms:list:previous', openPrev.bind(this));
            app.off('cms:menu:forceRefresh');
            app.on('cms:menu:forceRefresh', refreshUI.bind(this));
        };


        // FUNCTIONS FOR SEARCH PANELS MANAGEMENT
        listTemplateCtor.prototype.togglePanel = togglePanel;
        listTemplateCtor.prototype.isAnyPanelVisible = getAnyPanelVisible;

        // FUNCTIONS FOR QUICK AND ADVANCED SEARCH
        listTemplateCtor.prototype.quickSearchCmd = quickSearchCmd;
        listTemplateCtor.prototype.resetQuickSearch = resetQuickSearch;
        listTemplateCtor.prototype.advancedSearchCmd = advancedSearchCmd;
        listTemplateCtor.prototype.resetSearch = resetSearch;

        listTemplateCtor.prototype.exportExcel = exportExcel;
        listTemplateCtor.prototype.piocher = piocher;
        listTemplateCtor.prototype.redeposer = redeposer;

        listTemplateCtor.prototype.changeAttribueA = changeAttribueA;

        // HANDLING OF LISTS IN INBOX TEMPLATES
        listTemplateCtor.prototype.prev = openPrev;
        listTemplateCtor.prototype.next = openNext;

        // VIEW LIFECYCLE and UI
        listTemplateCtor.prototype.activate = viewActivate;
        listTemplateCtor.prototype.compositionComplete = viewCompositionComplete;

        listTemplateCtor.prototype.refreshListUI = refreshListUI;
        listTemplateCtor.prototype.refreshUI = refreshUI;

        // PAGER, SORTING, AND OTHER LIST RELATED EVENTS
        listTemplateCtor.prototype.goToPage = goToPage;
        listTemplateCtor.prototype.orderBy = orderBy;
        listTemplateCtor.prototype.isSortedBy = isSortedBy;
        listTemplateCtor.prototype.canSort = canSort;
        listTemplateCtor.prototype.linkClick = linkClick;
        listTemplateCtor.prototype.itemClick = openItem;
        listTemplateCtor.prototype.isRowActive = isRowActive;
        listTemplateCtor.prototype.toggleAllChecks = toggleAllChecks;

        // UTILITY FUNCTIONS TO MANAGE THE URL (BOOKMARKING)
        listTemplateCtor.prototype.updateRouter = updateRouter;
        listTemplateCtor.prototype.wrapQuery = wrapQuery;
        listTemplateCtor.prototype.unWrapQuery = unWrapQuery;

        // DATA
        listTemplateCtor.prototype.fetchItems = fetchItems;
        listTemplateCtor.prototype.getOptions = getOptions;

        return listTemplateCtor;

        /**
         * when click on arrow up in inbox view, change to previous item
         */
        function openPrev() {

            // if both previous and next have the same value, it means they're null and we're out of context
            if (this.previousItemIndex() === this.nextItemIndex()) {
                notifier.alert(i18n.t('cms:error.outOfContext'));
                return;
            }

            // no previous item (computed by setPreviousItem())
            if (this.previousItemIndex() === null || this.paging.pageNbr() <= 0) {
                notifier.info(i18n.t('cms:error.noPrevItem'));
                router.navigate(router.activeInstruction().config.hash + '/liste/' + this.type);
                return;
            }

            if (this.previousItemIndex() < 0) {
                // has to change page
                notifier.log('Paged down..');
                this.goToPage({ nb: this.paging.pageNbr() - 1 })
                    .then(function () {
                        // take the last one of the list and open it
                        var last = this.items().length - 1;
                        openItem.call(this, this.items()[last], last);
                    }.bind(this));

            } else {
                var p = this.items()[this.previousItemIndex()];

                openItem.call(this, p, this.previousItemIndex());
            }
        }

        /**
         * when click on arrow down in inbox view, change to next item
         */
        function openNext() {

            // if both previous and next have the same value, it means they're null and we're out of context
            if (this.previousItemIndex() === this.nextItemIndex()) {
                notifier.alert(i18n.t('cms:error.outOfContext'));
                return;
            }

            // no next item (computed by setNextItem())
            if (this.nextItemIndex() === null) {
                notifier.info(i18n.t('cms:error.noNextItem'));
                router.navigate(router.activeInstruction().config.hash + '/liste/' + this.type);
                return;
            }

            if (this.nextItemIndex() >= this.items().length) {
                // has to change page
                notifier.log('Paged up..');
                this.goToPage({ nb: this.paging.pageNbr() + 1 })
                    .then(function () {
                        openItem.call(this, this.items()[0], 0);
                    }.bind(this));

            } else {
                var p = this.items()[this.nextItemIndex()];
                openItem.call(this, p, this.nextItemIndex());
            }
        }

        /**
         * retrieve the correct list of options for a select element
         */
        function getOptions(name) {
            return appConfig.getValues(name) || [];
        }

        /**
         * return the list in excel format
         */
        function exportExcel() {
            notifier.log('NOT IMPLEMENTED');
        }

        function refreshUI() {
            var topMenuId = router.activeInstruction().config.opusId;
            var topMenuHash = router.activeInstruction().config.hash;

            menu.getSubMenu(topMenuId, true)
                .then(menu.bindLeftMenu.bind(menu, this.type, topMenuId, topMenuHash))
                .then(bindItemList.bind(this, this.type));
        }

        /**
         * Piocher les messages
         */
        function piocher() {
            var that = this;
            if (this.type === 'messagesRequisitionsStandards') {
                ds.multiPiocher(user.initials).then(function(results) {
                    if (!_.isEmpty(results.errors)) {
                        throw new Error(errors);
                    }
                }).then(refreshUI.bind(that)).fail();
            } else {
                ds.piocherMessage(user.initials, this.type).then(function(results) {
                    if (!_.isEmpty(results.errors)) {
                        throw new Error(errors);
                    }
                }).then(refreshUI.bind(that)).fail();
            }
        }

        /**
         * Redéposer messages pioche
         */
        function redeposer() {
            var that = this;
            if (this.type === 'messagesRequisitionsStandards') {
                ds.multiRedeposer(user.initials).then(function (results) {
                    if (!_.isEmpty(results.data)) {
                        throw new Error(results.data);
                    }
                }).then(refreshUI.bind(that));
            } else {
                ds.redeposerMessage(user.initials, this.type).then(function (results) {
                    if (!_.isEmpty(results.data)) {
                        throw new Error(results.data);
                    }
                }).then(refreshUI.bind(that));
            }
        }

        /**
         * Add or remove attribue à moi checkbox in advanced search field
         */
        function changeAttribueA() {
            if (this.isAttribueMoi() && (typeof this.advancedSearchFields().attribueEq !== 'undefined')) {
                this.advancedSearchFields().attribueEq(user.initials);
            } else {
                this.advancedSearchFields().attribueEq(null);
            }
        }

        /**
         * Change the current page
         */
        function goToPage(page) {

            this.paging.pageNbr(page.nb);

            return this.fetchItems();
        }

        /**
         * Transform query string in the URL in actual variable usable by this viewmodel
         */
        function unWrapQuery(queryObj) {
            if (queryObj) {
                if (queryObj.qs) {
                    this.quicksearch(queryObj.qs);
                } else if (queryObj.as) {
                    this.quicksearch('');

                    delete queryObj.as;

                    ko.mapping.fromJS(queryObj, this.advancedSearchFields());
                }
            }
        }

        /**
         * Transforms some of this viewmodel's property (such as quicksearch or advancedSearchFields)
         * into an actual query string that can be added to the URL
         */
        function wrapQuery() {

            if (this.quicksearch()) {
                return {
                    qs: this.quicksearch()
                };
            } else {
                var as = ko.mapping.toJS(this.advancedSearchFields);
                // no quicksearch? was certainly an advanced search
                var wrappedAS = {
                    as: 1
                };
                for (var p in as) {
                    if (as.hasOwnProperty(p) && as[p] !== null && typeof as[p] !== 'undefined') {
                        wrappedAS[p] = as[p];
                    }
                }
                return wrappedAS;
            }
        }

        /**
         * Updates the URL with the current visible item, and maybe a query string (if any..)
         * without triggering navigation (bookmarking purpose)
         */
        function updateRouter() {

            var urlQuery = this.wrapQuery();

            var url = router.activeInstruction().config.hash + '/liste/' + this.type;
            if (this.visibleItem()) {
                url += '/' + this.visibleItem().type + '/' + this.visibleItem().id;
            }

            if (urlQuery) {
                url += '?' + $.param(urlQuery);
            }

            router.navigate(url, false);

            return url;
        }

        /**
         * reacts to the click event from the checkbox in the list header
         */
        function toggleAllChecks() {
            // the click event occurs before checked toggle
            var newState = !this.globalCheck();

            if (newState) {
                this.checks(_.map(
                    _.range(this.items().length), function (i) {
                        return '' + i;
                    }));
            } else {
                this.checks([]);
            }

            // allow click to bubble to checked event
            return true;
        }

        /**
         * an active row is the one that was clicked on to display an item
         */
        function isRowActive(row) {
            return (this.visibleItem() && parseInt(row[this.primaryColumnIndex()], 10) === parseInt(this.visibleItem().id, 10));
        }

        /**
         * display the corresponding item
         */
        function openItem(item, i, col) {

            setPreviousItem.call(this, i - 1);
            setNextItem.call(this, i + 1);

            breadcrumb.removeDetailCrumbs();
            hideItemlistInMessageDetail();
            this.currentVisiblePanel('');
            this.visibleItem({ id: item[this.primaryColumnIndex()], type: item[this.itemTypeColumnIndex()] });
            this.updateRouter();
        }

        /**
         * setup previous item pointed by 'down' arrow in inbox mode
         */
        function setPreviousItem(i) {
            this.previousItemIndex(i);

            if (this.paging.pageNbr() <= 1 && i < 0) {
                this.previousItemIndex(null);
            }
        }

        /**
         * setup next item pointed by 'up' arrow in inbox mode
         */
        function setNextItem(i) {
            this.nextItemIndex(i);

            var nbPages = Math.ceil(this.totalItemCount() / this.paging.pageSize);

            if (i >= this.items().length && this.paging.pageNbr() >= nbPages) {
                this.nextItemIndex(null);
            }
        }

        /**
         * When we click on a link (left click), we want to navigate to it in the detail view
         */
        function linkClick(item, col) {
            notifier.log(item, col);
            notifier.log('NOT IMPLEMENTED');
        }


        /**
         * tells if the column id is sortable or not
         */
        function canSort(id) {
            return this.noSortColumns().indexOf(id) < 0;
        }

        /**
         * Tells each header if it is sorted and in which direction
         */
        function isSortedBy(id, dir) {
            return this.sorting().id() === id
                && this.sorting().dir() === dir;
        }

        /**
         * order by event originated from a click on the header
         */
        function orderBy(column) {
            // do nothing if column can't be sorted
            if (this.noSortColumns().indexOf(column.id) >= 0) {
                return;
            }

            // reverse if click on same column
            if (this.sorting().id() === column.id) {
                this.sorting().dir(this.dirs.invert[this.sorting().dir()]);
            }

            this.sorting().id(column.id);

            this.fetchItems();
        }

        function viewCompositionComplete(view) {

            // TODO [dge, 1] finish code for splitter if necessary in this module... (validate with FBY)
            /*$('.op-list-short', view).resizable( {
             autoHide: false,
             handles: { 'e': $('#resizeHandle') },
             minWidth: 300,
             maxWidth: 600,
             resize: function(e, ui) {
             var parent = ui.element.parent();
             var remainingSpace = parent.width() - ui.element.outerWidth(),
             divTwo = ui.element.next(),
             divTwoWidth = (remainingSpace - (divTwo.outerWidth() - divTwo.width()))/parent.width()*100+"%";

             divTwo.width(divTwoWidth);
             // Override jQuery default behaviour
             //ui.element.css({left: 0});
             },
             stop: function(e, ui) {
             var parent = ui.element.parent();
             ui.element.css({ width: ui.element.width()/parent.width()*100+"%" });
             }
             });*/
        }

        /**
         * View lifecycle
         */
        function viewActivate(type, itemType, id, queryObj) {

            L.needsRelayout();
            lifecycle.registerFinalProcess('refreshListUI', refreshListUI.bind(this));

            // id and itemType are set only if a detail form is shown, or if we want a new entity (empty form with no list)
            // in the route => we have to check if they're present or not
            var item = null;
            if (typeof itemType === 'object') {
                queryObj = itemType;
            } else if (itemType || id) {
                item = { id: id, type: itemType };
            }

            // what is the currently selected top menu ?
            var topMenuId = router.activeInstruction().config.opusId;
            var topMenuHash = router.activeInstruction().config.hash;

            // do things in order, because almost everything here is loaded asynchronously
            // begin by retrieving the left menu (we'll need that info to know the list types available)
            return menu.getSubMenu(topMenuId, false)
                // bind the menu, and retrieve the configuration of the list (from the first menu item if type is not yet provided)
                .then(menu.bindLeftMenu.bind(menu, type, topMenuId, topMenuHash))
                .then(getCurrentMenu.bind(this))
                .then(getConfig.bind(this))
                // once we have the type for sure, bind the advanced search template
                .then(bindAdvancedSearch.bind(this))
                // bind the default filter temple
                .then(bindDefaultFilter.bind(this))
                // .. then bind the corresponding actions for the type
                .then(bindActions.bind(this))
                // and finally retrieve and bind the items (if there is a query)
                .then(bindItemList.bind(this, queryObj))
                // update the current item if it was provided in the queryObj
                .then(function () {
                    this.visibleItem(item);
                }.bind(this))
                .fail(notifier.error);
        }

        /**
         * Get the current left menu
         */
        function getCurrentMenu(type) {
            // if no type selected, select first one
            if (!type) {
                type = menu.leftMenu()[0].id;
            }

            // Get the current menu by type
            this.currentMenu(_.findWhere(menu.leftMenu(), {id: type}));
            if (!this.currentMenu()) {
                for (i = 0; i < menu.leftMenu().length; i++) {
                    this.currentMenu(_.findWhere(menu.leftMenu()[i].children, {id: type}));
                    if (this.currentMenu()) {
                        break;
                    }
                }
            }

            return type;
        }

        /**
         * Retrieves the menu from the topMenuId, bind it, determine the type (if not provided it will be the first of the list
         */
        function getConfig(type) {
            // list type to display is now determined for sure!
            this.type = type;
            // we can retrieve the config!
            this.config = appConfig.getListTemplates(type);
            // determine if list is inbox or search based from the template it uses
            this.isInboxBased(this.config.type === 'inbox');

            return type;
        }

        /**
         * retrieve fields from type definition and bind them to the advanced search template
         */
        function bindAdvancedSearch(type) {
            return appConfig.getSearchFields(type)
                .then(function (advancedFieldsDefinition) {
                    this._defaultASFields = advancedFieldsDefinition;
                    this.advancedSearchFields(ko.mapping.fromJS(advancedFieldsDefinition));
                    return type;
                }.bind(this));
        }

        /**
         * retrieve fields from type definition and bind them to the default filter template
         */
        function bindDefaultFilter(type) {
            // Using for Messages screen. Show all messages that match a criteria in filter template
            // For ex: lockOwner == currentUser, estTraite == false
            return appConfig.getFilterFields(type)
                .then(function (filterFieldsDefinition) {
                    // Backup the default value of filter
                    this._defaultFilterFields = filterFieldsDefinition;
                    // Add filters to obseravble object
                    this.defaultFilterFields(ko.mapping.fromJS(filterFieldsDefinition));
                    return type;
                }.bind(this));
        }

        /**
         * loads actions modules and stores them in actions prop
         */
        function bindActions(type) {
            require(this.config.actions, function () {

                // for each action, create the action instance
                this.actions = _.map(arguments, function (action) {
                    return new action();
                });

            }.bind(this), function (err) {
                notifier.error(new Error(i18n.t('cms:error.actionsNotFound')));
            });

            return type;
        }

        /**
         * retrieve item following query
         */
        function bindItemList(query, type) {

            breadcrumb.setRoot(type);

            // if it's a search template and there is no query, nothing to do here (item list is empty by default)
            if (!this.isInboxBased() && !query) {
                return null;
            }

            // retrieve the parameters of the query
            this.unWrapQuery(query);
            // Add filter for Messages screen
            if (this.advancedSearchFields && router.activeInstruction().config.opusId === 'messages') {
                var defaultFilterFields = ko.mapping.toJS(this.defaultFilterFields);

                if (this.currentMenu() && this.currentMenu().estMasse) {
                    defaultFilterFields.lockOwnerEq = user.initials;
                    defaultFilterFields.estTraiteEq = false;
                } else {
                    defaultFilterFields.estTraiteEq = false;
                }

                var advancedSearchFields =  _.extend(defaultFilterFields, ko.mapping.toJS(this.advancedSearchFields));
                this.advancedSearchFields(ko.mapping.fromJS(advancedSearchFields));
            }

            // get items and show them in the list
            return fetchItems.call(this);
        }

        /**
         * Do things in order: retrieve column definition, default sort config
         * then build query for API, and eventually get list of item
         */
        function fetchItems() {
            var vm = this;
            var type = this.type;

            return appConfig.getColumns(type)
                .then(function (columnsResult) {

                    // once retrieved, get the default sorter from the definition
                    return appConfig.getDefaultListSort(type)
                        .then(function (sortObj) {

                            // update the sorting object if it's not already setup
                            if (!vm.sorting()) {
                                vm.sorting(ko.mapping.fromJS(sortObj));
                            }
                            // returns the object for the query to be executed
                            return ds.buildQueryOptions(
                                vm.quicksearch(),
                                vm.searchInboxOnly(),
                                ko.mapping.toJS(vm.sorting),
                                ko.toJS(vm.paging),
                                ko.mapping.toJS(vm.advancedSearchFields));
                        })
                        .then(function (query) {
                            // with the query, retrieve the item list and setup the page
                            return ds.getListForType(type, query).then(function (itemsResult) {

                                vm.columns(columnsResult);
                                vm.items(itemsResult.results);

                                vm.totalItemCount(itemsResult.count);

                                // refresh some UI related logic like row highlight
                                vm.refreshListUI();

                                breadcrumb.setSearch(type);
                            });
                        });
                })
                .fail(function (err) {
                    notifier.error(err);
                });
        }

        /**
         * make hover things work with double-lined table
         */
        function refreshListUI() {

            var trHoverCl = 'op-row-hover-dual';

            var $rows = $('.op-table-hover-dual tr');

            $rows.hover(function () {
                var css = $(this).hasClass('op-row-even') ? '.op-row-even' : '.op-row-odd';

                $rows.not(css).removeClass(trHoverCl);

                $(this)
                    .prev(css)
                    .andSelf()
                    .next(css)
                    .addBack()
                    .addClass(trHoverCl);
            }, function () {
                $(this).siblings().andSelf().removeClass(trHoverCl);
            });
        }

        /**
         * Tells if any pane is currently visible (advanced search,
         */
        function getAnyPanelVisible() {
            return _.reduce(this.panels, function (memo, val) {
                return memo || val;
            }, false);
        }

        /**
         * Set quicksearch to ''
         */
        function resetQuickSearch() {
            this.visibleItem(null);
            this.items([]);
            this.columns([]);
            this.totalItemCount(null);
            this.quicksearch('');
            this.updateRouter();
        }

        /**
         * reset view and launch the quisearch
         * the value of the quicksearch is bound 2-way, this is just a trigger function to launch
         * the process
         */
        function quickSearchCmd() {
            this.currentVisiblePanel('');
            this.visibleItem(null);
            // empty advancedSearch fields.
            ko.mapping.fromJS(this._defaultASFields, this.advancedSearchFields());

            // If user check "Dans mes messages seulement"
            if (this.searchInboxOnly()) {
                var defaultFilterFields = ko.mapping.toJS(this.defaultFilterFields);
                // Add filter lockOwnerEq = current user
                if (defaultFilterFields && (typeof defaultFilterFields.lockOwnerEq !== 'undefined')) {
                    defaultFilterFields.lockOwnerEq = user.initials;
                }

                // Mixing filter to advanced search criteria
                // Because on the backend, filter and advanced search binding on the same searchCriteriaDto.getFilters()
                var advancedSearchFields =  _.extend(ko.mapping.toJS(this.advancedSearchFields), defaultFilterFields);
                this.advancedSearchFields(ko.mapping.fromJS(advancedSearchFields));
            } else {
                // Remove all value of filters fields & advancedSearch fields
                ko.mapping.fromJS(this._defaultASFields, this.advancedSearchFields());
                var advancedSearchFields = _.extend(ko.mapping.toJS(this.advancedSearchFields()), this._defaultFilterFields);
                ko.mapping.fromJS(advancedSearchFields, this.advancedSearchFields());
            }

            this.fetchItems();

            this.updateRouter();

            L.relayout();
        }

        /**
         * respond to panel click (Recherche avancée, Filtres, Actions groupées)
         */
        function togglePanel(panel) {
            if (this.currentVisiblePanel() === panel) {

                this.currentVisiblePanel('');

                this.globalCheck(false);
                this.checks([]);
                this.showBulkSelector(false);

            } else {
                this.currentVisiblePanel(panel);
                this.showBulkSelector(this.items().length > 0 && panel === 'actions');
            }

            L.relayout();
        }

        function resetSearch() {
            this.visibleItem(null);
            this.items([]);
            this.columns([]);
            this.totalItemCount(null);
            this.quicksearch('');
            // empty advancedSearch fields
            ko.mapping.fromJS(this._defaultASFields, this.advancedSearchFields());
            this.updateRouter();
        }

        /**
         * Triggers the advancedSearch process (values are 2-way bound with the form)
         */
        function advancedSearchCmd() {
            // empty default filter value in advanced search fields
            var advancedSearchFields = _.extend(ko.mapping.toJS(this.advancedSearchFields()), this._defaultFilterFields);
            ko.mapping.fromJS(advancedSearchFields, this.advancedSearchFields());
            this.visibleItem(null);
            this.quicksearch('');
            this.fetchItems();
            this.updateRouter();
        }
            function hideItemlistInMessageDetail() {
                $('.hidein-message-details').hide();
            }
    }
)
;
